<?php
/**
 * Classe de gestion simple des requêtes web If-Modified-Since
 * 
 * La gestion n'est à faire qu'une fois par page au maximum, donc 
 * l'implémentation est naturellement faite en singleton.
 *
 * @package    HTTP
 * @author     Philippe MARASSE <philippe.marasse@vienneinfo.org>
 * @copyright  2007 Philippe MARASSE
 * @licence     http://www.opensource.org/licenses/gpl-license.php
 * @version    $Id: class.dirapHttpIMS.inc.php,v 1.1 2007/07/12 08:01:08 philippe Exp $
 *
 * Objet PHP5 - Implémenté avec le DP Singleton
 *
 * Merci à http://ontosys.com/php/cache.html pour la technique de cache... 
 * et à la doc sur php.net pour mettre un Timestamp au format RFC 1123 
 * ( http://fr2.php.net/manual/en/function.gmdate.php )
 * Le reste des bidouilles est de mon fait :-)
 *
 * Définir la constante DIRAP_DEBUG pour bypasser les requêtes If-Modified-Since
 * Un entête http 'X-IMS-Info' et continue le traitement.
 */
class dirapHttpIMS {

    /**
     * @var object Instace du singleton
     * @access private
     */
    private static $instance = null;

    /**
     * @var integer timestamp de dernière modification
     */
    private $lastmod = 0;

    /**
     * Constructeur privé (singleton)
     *
     * @return jpbIMSHelper Instance de l'objet
     */
    private function __construct() {
        // Par défaut, on met le IMS à la date de dernière modif de la classe
        $this->lastmod = filemtime(__FILE__);
    }

    /**
     * Récupération de l'instance du singleton
     *
     * @return jpbIMSHelper Instance du singleton
     */
    public static function getInstance() {
        if (! isset(self::$instance)) {
            self::$instance = new dirapHttpIMS();
        }
        return (self::$instance);
    }

    /**
     * Récupère le timestamp de dernière modification
     *
     * @return integer timestamp de dernière modification
     */
    public function getTimestamp() {
        return ($this->lastmod);
    }

    /**
     * Fixe un nouveau timestamp de dernière modification.
     * On devrait rarement l'utiliser puisque par défaut, le $this->lastmod
     * a déjà une valeur correspondant à quelque chose de concret
     *
     * @param integer $in_ts Timestamp de dernière modification (>0 !)
     * @return boolean Vrai si tout s'est bien déroulé
     */
    public function setTimestamp($in_ts) {
        if (is_numeric($in_ts)) {
            $this->lastmod = intval($in_ts) > 0 ? intval($in_ts) : 0;
            return(true);
        }
        return(false);
    }

    /**
     * Fixe un nouveau timestamp de dernière modification s'il est plus 
     * récent
     *
     * @param integer $in_ts Timestamp de dernière modification (>0 !)
     * @return boolean Vrai si le nouveau TS était plus récent
     */
    public function setNewerTimestamp($in_ts) {
        if (is_numeric($in_ts)) {
            $lc_ts = intval($in_ts);
            if ($lc_ts > $this->lastmod) {
                $this->lastmod =   $lc_ts;
                return(true);
            }
        }
        return(false);
    }

    /**
     * Répond à une éventuelle requête HTTP IfModifiedSince
     *
     * @param boolean $in_exit Vrai si on doit arrêter l'exécution en cas de succès
     * @return boolean Vrai si IMS Hit, faux si la page est plus récente.
     *
     * Il s'agit de comparer la date de dernière modification de la page 
     * (stocké dans $this->lastmod) et la date de dernière modification fournie 
     * dans la requête HTTP par le champ If-Modified-Since
     *
     * Si la page est plus récente, il faut la recalculer, sinon, on renvoie au
     * navigateur un message HTTP 304/Not Modified et on peut arrêter 
     * l'exécution ici (comportement par défaut).
     */
    public function doIfModifiedSince($in_exit = true) {

        // Sessions et requêtes IMS sont incompatibles 
        $lc_sid = session_id();
        if (!empty($lc_sid)) {
            header('X-IMS-Info: IMS et Session incompatibles');
            return(false);
        }

        $lc_now = time();

        // Récupère le timestamp à partir de l'entête If Modified Since: si existant...
        if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
            $lc_ims = preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE']);
            $lc_imstime=strtotime($lc_ims);
            // Vérifie que l'IMS demandé n'est pas dans le futur (bug de page ?) 
            // dans ce cas, on ignore.
            $lc_imstime= ($lc_imstime > $lc_now) ? 0 : $lc_imstime;
        } else {
            // Sinon de base on fixe IMS au 01/01/1970
            $lc_imstime=0;
        }
         
        $lc_mtime = ($this->lastmod > $lc_now) ? $lc_now : $this->lastmod;

        // En mode Debug : on ne traite pas les requêtes IMS
        if (defined('DIRAP_DEBUG')) {
            header('X-IMS-Info: LastMod '.gmdate('D, d M Y H:i:s T', $lc_mtime));
            return(false);
        }

        // Comparaison de date de dernière modification, si elle est inférieure :
        // la page est identique.
        if ($lc_mtime <= $lc_imstime) {
            if (isset($_SERVER['SERVER_PROTOCOL'])) {
                $lc_proto = $_SERVER['SERVER_PROTOCOL'];
            } else {
                $lc_proto = 'HTTP/1.1';
            }
            header($lc_proto.' 304 Not Modified');
            if ($in_exit) {
                exit(0);
            } else {
                return(true);
            }
        }
        header('Last-Modified: '.gmdate('D, d M Y H:i:s T', $lc_mtime));
        return(false);
    }

} // class

?>
